from datetime import datetime
from typing import Optional
from pydantic import BaseModel, Field


class NetInterface(BaseModel):
    host: str = Field(title="ip地址")
    port: int = Field(title="网络端口")


class SerialInterface(BaseModel):
    com: str = Field(title="串口号")
    baud: int = Field(title="波特率")
    stopBit: int = Field(title="停止位")
    dataBit: int = Field(title="数据位")
    parity: int = Field(title="校验位")


class Communication(BaseModel):
    brand: str = Field(title="PLC厂家名称")
    model: str = Field(title="PLC型号")
    protocol: str = Field(title="协议")


class Point:
    def __init__(self, name: str, func: str, addr: int, desc: str = ""):
        self.__name = name
        self.__func = func
        self.__addr = int(addr)
        self.__desc = desc
        self.__last = None
        self.__value = None
        # 缓存值：周期开始时缓存 __value 的初始值
        self.__bufValue = None      
        self.__updateAt = None
        # 预更新时间：数据发生变化时暂存变化时间，在周期开始时更新至 __update 中
        self.__preUpdateAt = None   
    
    @property
    def name(self):
        return self.__name
    
    @property
    def func(self):
        return self.__func
    
    @property
    def addr(self):
        return self.__addr
    
    @property
    def desc(self):
        return self.__desc
    
    @property
    def last(self):
        return self.__last

    @property
    def value(self):
        return self.__value

    @property
    def updateAt(self):
        return self.__updateAt

    @value.setter
    def value(self, v):
        self.__value = v

    def change(self):
        """ 判断数值是否发生变化 """
        return self.__value != self.__bufValue

    def beforeListen(self):
        """ 监听启动后完成初次采集时调用一次 """
        self.__value = self.__last = self.__value
        self.__preUpdateAt = datetime.now()

    def beforeUpdate(self):
        self.__bufValue = self.__value
        self.__updateAt = self.__preUpdateAt

    def updated(self):
        """ 更新数据 """
        if self.change():
            self.__preUpdateAt = datetime.now()
            self.__last = self.__value

    def __repr__(self) -> str:
        return f"<Point object> name: {self.name}, func: {self.func}, addr: {self.addr}, desc: {self.desc}"

class PointGroup:
    def __init__(self, points: list[Point], name: Optional[str] = ""):
        if name == "name":
            raise AttributeError(f"name为分组默认属性，不允许使用")
        self.__name = name
        self.__points = {}
        for point in points:
            if not isinstance(point, Point):
                raise AttributeError(f"传入点位组的对象应为Point类型，但接收的是{type(point)}类型")
            self.__points[point.name] = point
    
    @property
    def name(self):
        return self.__name

    @property
    def point(self):
        return self.__points
    
    @property
    def p(self):
        return self.__points

    def any(self, value: bool | int):
        return any([self.__points[p].value == value for p in self.__points])
    
    def all(self, value: bool | int):
        return all([self.__points[p].value == value for p in self.__points])
    
    def anyChange(self):
        return any([self.__points[p].change() for p in self.__points])


class DeviceModel:
    def __init__(self, no: str, name: str, groups: list[PointGroup]):
        self.__no = no
        self.__name = name
        self.__groups = {}
        self.__points = {}
        for g in groups:
            if not isinstance(g, PointGroup):
                raise AttributeError(f"传入对象应为PointGroup类型，而不是{type(g)}")
            self.__groups[g.name] = g
            for p in g.p:
                self.__points[f"{g.name}:{p}"] = g.p[p]

    @property
    def no(self):
        return self.__no

    @property
    def name(self):
        return self.__name
    
    @property
    def group(self):
        return self.__groups
    
    @property
    def g(self):
        return self.__groups
    
    @property
    def point(self):
        return self.__points
    
    @property
    def p(self):
        return self.__points

    def any(self, value: bool | int):
        return any([self.__points[p].value == value for p in self.__points])
    
    def all(self, value: bool | int):
        return all([self.__points[p].value == value for p in self.__points])
    
    def anyChange(self):
        return any([self.__points[p].change() for p in self.__points])
    